From b682d065a3a9c9d881b4834e15d55853d88a67e3 Mon Sep 17 00:00:00 2001 From: Viet-Tam Luu Date: Thu, 30 Nov 2017 16:53:58 -0800 Subject: [PATCH] Support Unicode environment variables. Create ugetenv() OS-abstraction to return a QString environment variable value. Use it in place of getenv() in most places. Update inifile.cc routines to use QString to support Unicode paths for the gpsbabel.ini file, also greatly streamlining the code. --- csv_util.cc | 6 +-- defs.h | 3 ++ html.cc | 2 +- inifile.cc | 110 ++++++++++++++++------------------------------------ util.cc | 13 +++++++ 5 files changed, 54 insertions(+), 80 deletions(-) diff --git a/csv_util.cc b/csv_util.cc index 0cc8a9a4f..2f42e5012 100644 --- a/csv_util.cc +++ b/csv_util.cc @@ -1215,11 +1215,11 @@ xcsv_parse_val(const char* s, Waypoint* wpt, const field_map_t* fmp, wpt->SetCreationTime(sscanftime(s, fmp->printfc, 1)); break; case XT_LOCAL_TIME: - if (getenv("GPSBABEL_FREEZE_TIME")) { + if (ugetenv("GPSBABEL_FREEZE_TIME").isNull()) { + wpt->creation_time += sscanftime(s, fmp->printfc, 0); + } else { /* Force constant time zone for test */ wpt->creation_time += sscanftime(s, fmp->printfc, 1); - } else { - wpt->creation_time += sscanftime(s, fmp->printfc, 0); } break; /* Useful when time and date are in separate fields diff --git a/defs.h b/defs.h index 0d57241b1..eec287d1f 100644 --- a/defs.h +++ b/defs.h @@ -931,6 +931,9 @@ void debug_mem_close(); FILE* xfopen(const char* fname, const char* type, const char* errtxt); +// OS-abstracting wrapper for getting Unicode environment variables. +QString ugetenv(const char* env_var); + // FIXME: case_ignore_strcmp() and case_ignore_strncmp() should probably // just be replaced at the call sites. These shims are just here to make // them more accomidating of QString input. diff --git a/html.cc b/html.cc index 6f8b2770e..4ae4467f8 100644 --- a/html.cc +++ b/html.cc @@ -260,7 +260,7 @@ data_write() // Don't write this line when running test suite. Actually, we should // probably not write this line at all... - if (!getenv("GPSBABEL_FREEZE_TIME")) { + if (ugetenv("GPSBABEL_FREEZE_TIME").isNull()) { gbfprintf(file_out, " \n", gpsbabel_version); } gbfprintf(file_out, " GPSBabel HTML Output\n"); diff --git a/inifile.cc b/inifile.cc index aa5e8b000..78b182410 100644 --- a/inifile.cc +++ b/inifile.cc @@ -20,6 +20,8 @@ #include "defs.h" #include "inifile.h" +#include +#include #include #include @@ -44,98 +46,55 @@ typedef struct inifile_section_s { #define DELTA_BUFSIZE 128 #define GPSBABEL_INIFILE "gpsbabel.ini" +#define GPSBABEL_SUBDIR ".gpsbabel" /* Remember the filename we used so we can include it in errors. */ -char* gbinipathname; +QString gbinipathname; -static char* -find_gpsbabel_inifile(const char* path) /* can be empty or NULL */ +static QString +find_gpsbabel_inifile(const QString& path) /* can be empty or NULL */ { - FILE* test; - char* buff; - int len; - - if (path == NULL) { - return NULL; - } - - len = strlen(path); - buff = (char*) xmalloc(len + 1 + strlen(GPSBABEL_INIFILE) + 1); - strcpy(buff, path); - if (len > 0) { - char test = buff[len - 1]; -#ifdef __WIN32__ - if ((test != '\\') && (test != ':')) { - strcat(buff, "\\"); - } -#else - if (test != '/') { - strcat(buff, "/"); - } -#endif - } - strcat(buff, GPSBABEL_INIFILE); - test = fopen(buff, "rb"); - if (test) { - fclose(test); - return buff; + if (path.isNull()) { + return QString(); } - xfree(buff); - return NULL; + QString inipath(QDir(path).filePath(GPSBABEL_INIFILE)); + return QFile(inipath).open(QIODevice::ReadOnly) ? inipath : QString(); } static gbfile* open_gpsbabel_inifile() { - char* name; - char* envstr; + QString name; + QString envstr; gbfile* res = NULL; - envstr = getenv("GPSBABELINI"); - if (envstr != NULL) { - FILE* test; - - test = fopen(envstr, "r"); - if (test != NULL) { - fclose(test); + envstr = ugetenv("GPSBABELINI"); + if (!envstr.isNull()) { + if (QFile(envstr).open(QIODevice::ReadOnly)) { return gbfopen(envstr, "r", "GPSBabel"); } warning("WARNING: GPSBabel-inifile, defined in environment, NOT found!\n"); return NULL; } - name = find_gpsbabel_inifile(""); /* PWD */ - if (name == NULL) { + name = find_gpsbabel_inifile(""); // Check in current directory first. + if (name.isNull()) { #ifdef __WIN32__ - name = find_gpsbabel_inifile(getenv("APPDATA")); - if (name == NULL) { - name = find_gpsbabel_inifile(getenv("WINDIR")); - } - if (name == NULL) { - name = find_gpsbabel_inifile(getenv("SYSTEMROOT")); - } + // Use &&'s early-out behaviour to try successive file locations: first + // %APPDATA%, then %WINDIR%, then %SYSTEMROOT%. + (name = find_gpsbabel_inifile(ugetenv("APPDATA"))).isNull() + && (name = find_gpsbabel_inifile(ugetenv("WINDIR"))).isNull() + && (name = find_gpsbabel_inifile(ugetenv("SYSTEMROOT"))).isNull(); #else - if ((envstr = getenv("HOME")) != NULL) { - char* path; - - path = (char*) xmalloc(strlen(envstr) + 11); - strcpy(path, envstr); - strcat(path, "/.gpsbabel"); - name = find_gpsbabel_inifile(path); - xfree(path); - } - if (name == NULL) { - name = find_gpsbabel_inifile("/usr/local/etc"); - } - if (name == NULL) { - name = find_gpsbabel_inifile("/etc"); - } + // Use &&'s early-out behaviour to try successive file locations: first + // ~/.gpsbabel, then /usr/local/etc, then /etc. + (name = find_gpsbabel_inifile(QDir::home().filePath(GPSBABEL_SUBDIR))). + isNull() + && (name = find_gpsbabel_inifile("/usr/local/etc")).isNull() + && (name = find_gpsbabel_inifile("/etc")).isNull(); #endif } - if (name != NULL) { + if (!name.isNull()) { res = gbfopen(name, "r", "GPSBabel"); - if (gbinipathname) { - xfree(gbinipathname); - } gbinipathname = name; } return res; @@ -171,7 +130,8 @@ inifile_load_file(gbfile* fin, inifile_t* inifile, const char* myname) cin = lrtrim(cin); } if ((*cin == '\0') || (cend == NULL)) { - fatal("%s: invalid section header '%s' in '%s'.\n", myname, cin, gbinipathname); + fatal("%s: invalid section header '%s' in '%s'.\n", myname, cin, + qPrintable(gbinipathname)); } sec = (inifile_section_t*) xcalloc(1, sizeof(*sec)); @@ -185,7 +145,8 @@ inifile_load_file(gbfile* fin, inifile_t* inifile, const char* myname) inifile_entry_t* entry; if (sec == NULL) { - fatal("%s: missing section header in '%s'.\n", myname,gbinipathname); + fatal("%s: missing section header in '%s'.\n", myname, + qPrintable(gbinipathname)); } entry = (inifile_entry_t*) xcalloc(1, sizeof(*entry)); @@ -306,10 +267,7 @@ inifile_done(inifile_t* inifile) } xfree(inifile); } - if (gbinipathname) { - xfree(gbinipathname); - gbinipathname = NULL; - } + gbinipathname.clear(); } int diff --git a/util.cc b/util.cc index 67a300ae0..fd567371a 100644 --- a/util.cc +++ b/util.cc @@ -258,6 +258,19 @@ xfopen(const char* fname, const char* type, const char* errtxt) return f; } +/* + * OS-abstracting wrapper for getting Unicode environment variables. + */ +QString ugetenv(const char* env_var) { +#ifdef __WIN32__ + // Use QString to convert 8-bit env_var argument to wchar_t* for _wgetenv(). + return QString::fromWCharArray( + _wgetenv((const wchar_t*) QString(env_var).utf16())); +#else + // Everyone else uses UTF-8 or some other locale-specific 8-bit encoding. + return QString::fromLocal8Bit(std::getenv(env_var)); +#endif +} /* * Allocate a string using a format list with optional arguments * Returns -1 on error. -- 2.30.2